package main

import (
	"fmt"
	"log"
	"strings"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

type User struct {
	gorm.Model
	Name       string `gorm:"default:galeone"`
	Age        uint8  `gorm:"default:18"`
	Birthday   time.Time
	CreditCard CreditCard
}

type CreditCard struct {
	gorm.Model
	Number string
	UserID uint
}

func singleInsertData(db *gorm.DB) {
	user := User{
		Name:     "leo",
		Age:      32,
		Birthday: time.Now(),
	}
	result := db.Create(&user)
	if result.Error != nil {
		log.Fatalf("failed created data!")
	}
	fmt.Printf("single data insert successfully: \nid: %v, name: %v, age: %v, birthday: %v, affected: %v\n",
		user.ID,
		user.Name,
		user.Age,
		user.Birthday,
		result.RowsAffected)
}

func singleInsertDataWithDefault(db *gorm.DB) {
	user := User{
		Birthday: time.Now(),
	}
	result := db.Create(&user)
	if result.Error != nil {
		log.Fatalf("failed created data!")
	}
	fmt.Printf("single data insert successfully: \nid: %v, name: %v, age: %v, birthday: %v, affected: %v\n",
		user.ID,
		user.Name,
		user.Age,
		user.Birthday,
		result.RowsAffected)
}

func selectInsertData(db *gorm.DB) {
	user := User{
		Name: "Kobe",
	}
	result := db.Select("Name").Create(&user)
	if result.Error != nil {
		log.Fatalf("failed select created data!")
	}
	fmt.Printf("single data insert successfully: \nid: %v, name: %v, age: %v, birthday: %v, affected: %v\n",
		user.ID,
		user.Name,
		user.Age,
		user.Birthday,
		result.RowsAffected)
}

func batchInsertData(db *gorm.DB) {
	users := []*User{
		{Name: "James", Age: 28, Birthday: time.Now()},
		{Name: "Messi", Age: 36, Birthday: time.Now()},
	}
	result := db.Create(users)
	if result.Error != nil {
		log.Fatalf("failed created data!")
	}
	fmt.Printf("batch insert data successfully: %v, affected: %d\n", result, result.RowsAffected)
	for _, user := range users {
		fmt.Printf("id: %v, name: %v, age: %v, birthday: %v\n",
			user.ID,
			user.Name,
			user.Age,
			user.Birthday,
		)
	}
}

func batchSizeInsertData(db *gorm.DB) {
	users := []*User{
		{Name: "Messi_1"},
		{Name: "Messi_2"},
		{Name: "Messi_3"},
		{Name: "Messi_4"},
		{Name: "Messi_5"},
		{Name: "Messi_6"},
		{Name: "Messi_7"},
		{Name: "Messi_8"},
	}
	result := db.CreateInBatches(users, 100)
	if result.Error != nil {
		log.Fatalf("failed insert data with createInBatches")
	}
	for _, user := range users {
		fmt.Printf("id: %v, name: %v, age: %v, birthday: %v\n",
			user.ID,
			user.Name,
			user.Age,
			user.Birthday,
		)
	}
}

func insertDataWithInterface(db *gorm.DB) {
	result := db.Model(&User{}).Create(map[string]interface{}{
		"Name": "kobe_1",
		"Age":  22,
	})
	if result.Error != nil {
		log.Fatalf("failed insert with insertDataWithInterface")
	}
}

func batchInsertDataWithInterface(db *gorm.DB) {
	result := db.Model(&User{}).Omit("ID").Create([]map[string]interface{}{
		{"Name": "kobe_2", "Age": 22},
		{"Name": "kobe_3", "Age": 23},
	})
	if result.Error != nil {
		log.Fatalf("failed insert with insertDataWithInterface: %v\n", result.Error)
	}
}

func associationInterData(db *gorm.DB) {
	user := User{
		Name:       "Jack",
		Age:        39,
		CreditCard: CreditCard{Number: "12345678"},
	}
	result := db.Create(&user)
	if result.Error != nil {
		log.Panicf("failed insert data with associationInterData: %v\n", result.Error)
	}
	fmt.Printf("id: %v, name: %v, age: %v, birthday: %v, number: %v\n",
		user.ID,
		user.Name,
		user.Age,
		user.Birthday,
		user.CreditCard.Number,
	)
}

func associationInterDataWithOmit(db *gorm.DB) {
	user := User{
		Name:       "Jack_1",
		Age:        39,
		CreditCard: CreditCard{Number: "12345678"},
	}
	result := db.Omit("CreditCard").Create(&user)
	if result.Error != nil {
		log.Panicf("failed insert data with associationInterData: %v\n", result.Error)
	}
	fmt.Printf("id: %v, name: %v, age: %v, birthday: %v, number: %v\n",
		user.ID,
		user.Name,
		user.Age,
		user.Birthday,
		user.CreditCard.Number,
	)
}
func main() {
	dsn := "host=localhost user=postgres password=123456 dbname=my-gorm-app port=5432 sslmode=disable TimeZone=Asia/ShangHai"
	db, err := gorm.Open(postgres.New(postgres.Config{
		DSN:                  dsn,
		PreferSimpleProtocol: true,
	}), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
			TablePrefix:   "gorm-",
			NameReplacer:  strings.NewReplacer("_", "-"),
		},
	})
	if err != nil {
		log.Fatalf("failed connect to database %v\n", err)
	}

	if err := db.AutoMigrate(&User{}, &CreditCard{}); err != nil {
		log.Fatalf("failed to migrate database schema: %v\n", err)
	}

	log.Println("Database connected and schema migrated successfully!")
	singleInsertData(db)
	singleInsertDataWithDefault(db)
	batchInsertData(db)
	selectInsertData(db)
	batchSizeInsertData(db)
	insertDataWithInterface(db)
	// batchInsertDataWithInterface(db)
	associationInterData(db)
	associationInterDataWithOmit(db)
}
